<?php
/**
 * Router
 * @filesource
 */
/**
 * Namespace
 */
namespace framework\engine;
/**
 * Dependencies
 */
use framework\Front;
use framework\library\Url;
use framework\library\Request;
use framework\library\Session;
/**
 * Dynamically creates fully qualified directory paths and namespaced class names
 * specifically for the Actions object.
 * 
 * This class is very tightly coupled to the Actions Class
 * 
 * 
 * @author Randy Casburn <randy@plurious.com>
 * @copyright Copyright Plurious Software, all rights reserved
 * @license MIT
 * @license http://www.opensource.org/licenses/mit-license.html
 * @link http://plurious.com/framework/documentation/classes/Front.html
 * @version 2.1
 * 
 * @uses User
 * @uses Config
 * @uses Registry
 * @uses Action
 * @uses Document
 * @uses Url
 * @uses Request
 */
class Router {

    /** holds the app name as context */
    protected $context;

    /** holds fully qualified file path to the controller name being processed */
    protected $controller;

    /** holds fully qualified namespaced class name */
    protected $class;

    /** hold requested method if a method was requested */
    protected $method;

    /** holds an array of method arguments pass along with route to method */
    protected $methodArgs = array();

    /** holds  fully qualified file path to the associated template */
    protected $template;

    /** holds the name of the view layout - this is the directory name for the view templates */
    protected $layout_name;

    /** holds the fully qualified file path to the model */
    protected $model;

    /**
     * Load up the config object for use here
     */
    public function __construct() {
        $this->config = $this->registry->get('config');
    }

    /**
     * Build and test routes to controller, model and template
     * 
     * @param string $route a route in the form [context/]controller[/method/arg/arg/...]
     * @return mixed false if controller not found array of method parameters otherwise 
     */
    public function buildRoutes($route) {
        if (!$route) {
            $route = DEFAULT_ROUTE;
        }
        $this->clearRoutes();
        $this->buildDirectories($route);
        // If controller doesn't exist try to find it in the default context
        if (!is_file($this->controller)) {
            $this->controller = str_replace($this->context, DEFAULT_CONTEXT, $this->controller);
            // If still no controller, force a 404 error out there
            if (!is_file($this->controller)) {
                return false;
            }
        }
        // If model doesn't exist try to find it in the default context
        if (!is_file($this->model)) {
            $this->model = str_replace($this->context, DEFAULT_CONTEXT, $this->model);
            // A model is not mandatory, so set the model to null if it is not found
            if (!is_file($this->model)) {
                $this->model = null;
            }
        }
        // If template doesn't exist try to find it in the default layout directory
        if (!is_file($this->template)) {
            $this->template = str_replace($this->layout_name, 'default', $this->template);
            if (!is_file($this->template)) {
                $this->template = null;
            }
        }
        // Return the array of arguments passed for the method
        return $this->methodArgs;
    }

    /**
     * Decomposes provded route and sets file paths for controller, model and template
     * while a fully qualified namespaced class name, the method to call inside that
     * class and any parameters to pass to the method. 
     * 
     * Sets: 
     * config->context, 
     * $this->controller,
     * $this->method,
     * $this->methodArgs,
     * $this->class, 
     * $this->model, 
     * $this->template
     * 
     * @param string $route a route in the form [context/]controller[/method/arg/arg/...].  The controller may stipulate a relative path
     */
    private function buildDirectories($route) {
        $routeParts = explode('/', $route);
        // We need the context for building up the paths
        // If the first element name has been set to a constant it MUST be a context
        // otherwise use the default
        if (is_dir(APPS_DIR. DIRECTORY_SEPARATOR . $routeParts[0])) {
            $context = strtolower(array_shift($routeParts));
        } else {
            $context = DEFAULT_CONTEXT;
        }
        $this->config->set('context', $context);
        $this->config->set('context_dir', SYSTEM_DIR . 'apps/' . $context . '/');
        // The next element (or first if context was not provided) must be the controller
        // or a path element provided as part of the controller
        $controllerName = array_shift($routeParts);
        // Now we can set the path to the controller by asserting the file is there
        // We loop until we find the file
        while (count($routeParts) != 0 &&
        !is_file(SYSTEM_DIR . 'apps/' . $context . '/controller/' . $controllerName . '.php')) {
            $controllerName .= '/' . array_shift($routeParts);
        }
        // Now we have found the controller so let's assign it
        $this->controller = SYSTEM_DIR . 'apps/' . $context . '/controller/' . $controllerName . '.php';
        // This is necessary for namespacing the controller properly
        $baseControllerName = ucfirst(basename($controllerName));
        $className = str_replace(basename($controllerName), $baseControllerName, $controllerName);
        $className = str_replace('/', '\\', $className);
        $this->class = basename(SYSTEM_DIR) . '\\apps\\' . $context . '\\controller\\' . $className;
        // There may or may not be a model file, but it must be in the same relative path as the 
        // controller...so we'll use the $controllerName from the base path of '/model/' instead of '/controller/'
        $this->model = SYSTEM_DIR . 'apps/' . $context . '/model/' . $controllerName . '.php';
        // The template directory is in the front end...
        $this->template = SYSTEM_DIR . 'apps/' . $context . '/template/' . $controllerName . TEMPLATE_EXTENSION;
        // Get the class method if it exists
        $this->method = array_shift($routeParts);
        if (!$this->method) {
            $this->method = 'index';
        } else {
            if (count($routeParts) > 0) {
                $this->methodArgs = $routeParts;
            }
        }
    }

    /**
     * Clears out the usefull data from the class.
     * 
     * Clears:
     * Controller file path
     * Model file path
     * Template file path
     * Class namespace name
     * Method name
     * Template name 
     *
     */
    private function clearRoutes() {
        $this->controller = null;
        $this->model = null;
        $this->template = null;
        $this->class = null;
        $this->template_name = null;
        $this->method = null;
    }

    /**
     * Creates and manages a list of breadcrumbs
     * 
     * Uses session to store data if session is available, otherwise store
     * the breadcrumbs in a cookie.
     */
    protected function breadcrumbs() {
        $url = $this->registry->get('url');
        $request = $this->registry->get('request');
        $session = $this->registry->get('session');
        if ($session) {
            $use_session = true;
            $previous = $session->get('breadcrumbs');
        } else {
            $use_session = false;
            $previous = unserialize($request->getCookie('breadcrumbs'));
        }
        $new = array();
        if ($previous) {
            for ($i = 0 ; $i < count($previous); $i++) {
                // Add each breadcrumb as long as it doesn't match the current route
                if ($previous[$i]['text'] !== basename($this->class)) {
                    $new[] = $previous[$i];
                } else {
                    // if crumb matches this route, end the breadcrumb list
                    break;
                }
            }
        }
        $separator = (count($new) === 0)? '' : $this->config->get('breadcrumb_separator');
        $new[] = array('separator' => $separator , 'href' => $url->link(basename($this->class)), 'text' => ucfirst(basename($this->class)));
        if ($use_session) {
            $session->set('breadcrumbs', $new);
        } else {
            $request->setCookie('breadcrumbs', serialize($new));
        }
    }

}

?>